/**
 * Definition of basic DBC entity.
 *
 * \file entity.hpp
 * \author tomby
 * \date 04-01-2009
 **/

/*  Copyright (C) 2009  Tomas 'tomby' Bily

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef __DBC_ENTITY__
#define __DBC_ENTITY__

namespace dbc {
  class Entity;
};

namespace boost {
  inline void intrusive_ptr_add_ref (dbc::Entity* e);
  inline void intrusive_ptr_release (dbc::Entity* e);
};

namespace dbc {

#define IS_DBC_TYPE(X, TYPE) ((X)->get_type ()->is_type_of (TYPE::get_ctype ()))
#define DBC_TYPE(X, TYPE) (static_cast<TYPE*> (X))
#define DBC_TYPE_PTR(X, TYPE) (static_cast<TYPE*> ((X).get ()))
#define DBC_TYPE_TYPE(TYPE) (TYPE::get_ctype ())

#define ENTITY_DEF(TYPE)					\
protected:							\
 static const dbc::Type ctype;					\
								\
public:								\
 typedef boost::intrusive_ptr<TYPE > Ptr;			\
 typedef TYPE Self;						\
								\
 inline static const dbc::Type* get_ctype ()			\
  { return &TYPE::ctype; }

#define ENTITY_IMPL0(TYPE, NAME)				\
  const dbc::Type TYPE::ctype (NAME)

#define ENTITY_IMPL1(TYPE, NAME, ATOR)				\
  const dbc::Type TYPE::ctype (NAME, DBC_TYPE_TYPE (ATOR))

#define ENTITY_IMPL2(TYPE, NAME, ATOR1, ATOR2)			\
  const dbc::Type TYPE::ctype (NAME, DBC_TYPE_TYPE (ATOR1), DBC_TYPE_TYPE (ATOR2))

#define ENTITY_CTOR_IMPL(TYPE)	\
  type = TYPE::get_ctype ()

  class Id
  {
    unsigned long idg;
    unsigned long idl;
    
    static unsigned long global_id;
    static unsigned long local_id_cnt;

  public:
    Id (unsigned long g, unsigned long l)
      : idg (g), idl (l)
    {}

    static unsigned long get_global_id ()
    { return global_id; }
    
    static unsigned long get_local_id ()
    { return ++local_id_cnt; }
  };

  class Type
  {
    const std::string name;
    unsigned long type_id;
    unsigned multi_ator : 1;
    union
    {
      const Type *  ator;
      const Type ** ators;
    } ancestors;

  public:
    Type (const char* iname);
    Type (const char* iname, const Type* ator);
    Type (const char* iname, const Type* ator1, const Type* ator2);
    Type (const char* iname, const Type* ator1, const Type* ator2, const Type* ator3);
    Type (const char* iname, const Type* ator1, const Type* ator2, const Type* ator3, const Type* ator4);

    inline bool is_type_of (Type const* t) const
    { return is_type_of (*t); }

    inline bool is_type_of (Type const& t) const
    { return is_type_of (t, this); }

    bool operator== (const Type& t) const
    { return type_id == t.type_id; }

    inline const std::string& get_name ()
    { return name; }

  private:
    static bool is_type_of (const Type& t, Type const* self);
  };


/// TODO:Allocation API via pool api of boost

  /*
   * Basic class for all entities
   */

  class Entity
  {
    ENTITY_DEF(Entity);
  
  protected:
    typedef unsigned long ref_t; 

    const Id		id;
    Type const*		type;
    ref_t		ref_counter;

    inline void ref ()
    { ref_counter ++; }

    inline ref_t unref ()
    { return --ref_counter; }

    inline ref_t get_ref ()
    { return ref_counter; }

    friend inline void boost::intrusive_ptr_add_ref (Entity*);
    friend inline void boost::intrusive_ptr_release (Entity*);

    Entity ()
      : id (Id::get_global_id (), Id::get_local_id ()), type (NULL), ref_counter (0)
    {}

  public:
    virtual ~Entity () {}

  public:
    inline const Id& get_id ()
    { return id; }
  
    inline Type const* get_type ()
    { return type; }

  };

  /*
   * Producer-Consumer Entity
   */
#define DBC_PC_IFACE_EMPTY				\
  virtual void consume (Products& products) {}		\
  virtual Products produce (ProductionMask mask) { return PCEntity::null; }

  class PCEntity : public Entity
  {
    ENTITY_DEF(PCEntity);

  public:
    typedef std::list<Ptr> Econtainer;
    typedef boost::shared_ptr<Econtainer> EcontainerPtr;
    typedef const Type* ProductionMask;

  private:
    EcontainerPtr	producers;
    EcontainerPtr	consumers;

  protected:
    PCEntity (bool alloc_pc_containers);

    inline EcontainerPtr& get_producers ()
    { return producers; }

    inline EcontainerPtr& get_consumers ()
    { return consumers; }
      
    inline void set_producers (EcontainerPtr& iproducers)
    { producers = iproducers; }

    inline void set_consumers (EcontainerPtr& iconsumers)
    { consumers = iconsumers; }

  public:
    class Products
    {
    public:
      typedef std::vector<Entity::Ptr>	Container;
      typedef boost::shared_ptr<Container> ContainerPtr;

    protected:
      Entity::Ptr	producer;
      ContainerPtr	products;

    public:
      Products ()
      {}

      Products (Entity::Ptr iproducer, ContainerPtr iproducts)
	: producer (iproducer), products (iproducts)
      {}

      inline Entity::Ptr get_producer ()
      { return producer; }

      inline Container::iterator begin ()
      { return products->begin (); }

      inline Container::iterator end ()
      { return products->end (); }

    };

  public:
    static Products null;

  public:
    PCEntity ()
    {}

    inline void add_consumer (Self* consumer)
    { consumers->push_back (consumer); }

    inline void add_producer (Self* producer)
    { producers->push_back (producer); }

    // Consumer
    virtual void consume (Products& products) = 0;
  
    // Producer
    virtual Products produce (ProductionMask mask) = 0;
  };

};

namespace boost {
  inline void intrusive_ptr_add_ref (dbc::Entity* e)
  { e->ref (); }

  inline void intrusive_ptr_release (dbc::Entity* e)
  { if (!e->unref ()) checked_delete (e); }
};


#endif
